#include "napi/native_api.h" 
#include "hilog/log.h" 
#include <cstring>
#include <thread>
#undef LOG_DOMAIN 
#undef LOG_TAG 
#define LOG_DOMAIN 0x3200 
#define LOG_TAG "MY_TAG" 
#include <math.h>
#include <time.h>
#include "uv.h"

struct CallbackContext {
    napi_env env = nullptr;
    napi_ref callbackRef = nullptr;
    int32_t retData = 0;
};

static napi_value Add(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);
    
//     double value2;
//     napi_get_value_double(env, args[2], &value2);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    auto func = [](void *) { OH_LOG_ERROR(LOG_APP, "====== call env clean up!!!"); }; // 关键代码 
    napi_add_env_cleanup_hook(env, func, nullptr); // 关键代码 
    return sum;

}
/**
 * // 获取当前日期的示例函数
 * @param env
 * @param info
 * @return
 */
static napi_value GetCurrentDate(napi_env env, napi_callback_info info) {
  struct tm *current_time;
  time_t now;
  char str_time[32];
  size_t str_time_len;
 
  // 获取当前时间
  now = time(NULL);
  // 转换为本地时间
  current_time = localtime(&now);
  // 格式化时间为字符串
  strftime(str_time, sizeof(str_time), "%Y-%m-%d", current_time);
  napi_value value;
  // 创建一个JavaScript字符串来表示日期值
  napi_create_string_utf8(env, str_time, strlen(str_time), &value);
  return value;
}
static bool Napi_addPropertyInt32(napi_env env,napi_value obj,const char *key,int32_t value){
    napi_value key_napi = nullptr;
    napi_status status = napi_create_string_utf8(env,key,NAPI_AUTO_LENGTH,&key_napi);
    napi_value value_napi = nullptr;
    status = napi_create_int32(env, value, &value_napi);//通过一个C的int32_t数据创建js Number。
    status = napi_set_property(env, obj, key_napi, value_napi);
    return true;
}
static napi_value CallbackToArkTs(napi_env env,napi_callback_info info){
    //获取ArcTs参数
    size_t argc =1;
//     napi_value args[1] = {nullptr};
    napi_value js_cb = nullptr;
    napi_get_cb_info(env,info,&argc,&js_cb,nullptr,nullptr);
    
    napi_value argv = nullptr;
    napi_create_object(env,&argv);
    Napi_addPropertyInt32(env,argv,"type",1);
    Napi_addPropertyInt32(env,argv,"index",2);
    
    //native 回调到ts层
    napi_value result = nullptr;
    napi_call_function(env, NULL, js_cb,  1, &argv, &result);
    
    //获取Ts修改后的object
    napi_value typeNumber = nullptr;
    napi_get_named_property(env, result, "type", &typeNumber);
    int32_t number;
    napi_get_value_int32(env, typeNumber, &number);
    
//     OH_LOG_DEBUG(LOG_APP,"修改后的type:%{public}d",number);
     OH_LOG_INFO(LOG_APP, "ArkTS侧修改后的type：%{public}d", number); 
    bool resultRe;
    napi_status stat= napi_is_buffer(env,typeNumber,&resultRe);//study napi_is_buffer
    if (stat != napi_ok) {
        return result;
    }
    if (resultRe) {
         OH_LOG_INFO(LOG_APP, "1>>：napi_is_buffer =true"); 
    } else {
        OH_LOG_INFO(LOG_APP, "1>>：napi_is_buffer =false"); 
    }
    #ifdef USE_MYMATH
      OH_LOG_INFO(LOG_APP, "1>>：%{public}d", 1); 
    #else
      OH_LOG_INFO(LOG_APP, "2>>：%{public}d", 2); 
    #endif
    
    #ifdef HAVE_LOG
      OH_LOG_INFO(LOG_APP, "HAVE_LOG >>：%{public}d", 1);
    #else
      OH_LOG_INFO(LOG_APP, "HAVE_LOG >>：%{public}d", 2); 
    #endif
    return result;
    
}

void StartThread(CallbackContext *context){
    //从缓存的env中获取ArkTs线程的loop
    uv_loop_s* loop = nullptr;
    napi_get_uv_event_loop(context->env, &loop);
    // work_cb 此回调在另一个普通线程中执行，用于处理异步或者耗时任务，回调执行完后执行下面的回调。本示例中无需执行任务。
    uv_work_t* work = new uv_work_t;
    context->retData =100;
    work->data = context;
    uv_queue_work(loop, work, [](uv_work_t* work){// work_cb 此回调在另一个普通线程中执行，用于处理异步或者耗时任务，回调执行完后执行下面的回调。本示例中无需执行任务。
        //做耗时操作
        CallbackContext* context = reinterpret_cast<CallbackContext*>(work->data);
       for (int i=0; i<100; i++) {
           context->retData = context->retData+i+1;
       }
       
    }, [](uv_work_t* work,int status){ // after_work_cb 此回调在env对应的ArkTS线程中执行。
        CallbackContext* context = reinterpret_cast<CallbackContext*>(work->data);
        napi_handle_scope scope = nullptr;
        napi_open_handle_scope(context->env, &scope);
        if (scope == nullptr) {
            return;
        }
        napi_value callBack = nullptr;
        napi_get_reference_value(context->env, context->callbackRef, &callBack);
        size_t argc = 1;
        napi_value argv = nullptr;
        napi_create_int32(context->env, context->retData, &argv);
        napi_value result = nullptr;
        napi_call_function(context->env, nullptr, callBack, argc, &argv, &result);
        napi_delete_reference(context->env,context->callbackRef);
        napi_close_handle_scope(context->env, scope);
        
        if (work !=nullptr) {
            delete work;
        }
        delete context;
    });
}

static napi_value NAPI_Global_queueWork(napi_env env, napi_callback_info info) {
    size_t argc =1;
    napi_value argv[1] = {nullptr};
    napi_value thisArg = nullptr;
    void* data = nullptr;
    napi_get_cb_info(env, info, &argc, argv, &thisArg, &data);
    
    if (argc <1) {
        napi_throw_error(env,"ParameterError", "one param expected!");
        return nullptr;
    }
    
    napi_valuetype valueType = napi_undefined;
    napi_typeof(env,argv[0], &valueType);
    if (valueType != napi_function) {
        napi_throw_error(env, "ParameterError", "function expected!");
        return nullptr;
    }
    
    auto asyncContext = new CallbackContext();
    asyncContext->env = env;
    napi_create_reference(env, argv[0], 1, &asyncContext->callbackRef);
    //抛到非js线程执行逻辑
    std::thread workThread(StartThread,asyncContext);
    workThread.detach();
    return nullptr;
}
static napi_value NAPI_Global_loadArkTsModuleByNdk(napi_env env, napi_callback_info info)
{
     napi_value result;
    //1. 使用napi_load_module加载Test文件中的模块
    napi_status status = napi_load_module(env, "ets/pages/CalledByNDk", &result);

    napi_value testFn;
    //2. 使用napi_get_named_property获取test函数
    napi_get_named_property(env, result, "test", &testFn);
    //3. 使用napi_call_function调用函数test
    napi_call_function(env, result, testFn, 0, nullptr, nullptr);

    napi_value value;
    napi_value key;
    std::string keyStr = "value";
    napi_create_string_utf8(env, keyStr.c_str(), keyStr.size(), &key);
    //4. 使用napi_get_property获取变量value
    napi_get_property(env, result, key, &value);
    return result;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"callbackToArkTs", nullptr, CallbackToArkTs, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getCurrentDate", nullptr, GetCurrentDate, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"queueWork", nullptr, NAPI_Global_queueWork, nullptr, nullptr, nullptr, napi_default, nullptr},
{ "loadArkTsModuleByNdk", nullptr, NAPI_Global_loadArkTsModuleByNdk, nullptr, nullptr, nullptr, napi_default, nullptr }};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
{
    napi_module_register(&demoModule);
}
